# include<fstream>
#include<iostream>
#include"ns3/core-module.h"
#include"ns3/network-module.h"
#include"ns3/internet-module.h"
#include"ns3/csma-module.h"
#include "ns3/point-to-point-module.h"
#include"ns3/internet-apps-module.h"
#include"ns3/ipv4-static-routing-helper.h"
#include"ns3/ipv4-routing-table-entry.h"
#include"ns3/tap-bridge-module.h"
#include "ns3/random-variable-stream.h"
#include "ns3/random-variable-stream-helper.h"
#include"ns3/applications-module.h"
#include "ns3/netanim-module.h"


#include "ns3/mpi-module.h"
using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("TapCsmaVirtualMachineExample");
void BandwidthTrace(void);
void ScheduleBw(void);
std::ifstream bwfile_delay("./scratch/delay.txt");
std::string delay;
std::ifstream bwfile_datarate("./scratch/bandwidth.txt");
std::string datarate;

void BandwidthTrace(void)
{
	getline(bwfile_delay, delay);
	getline(bwfile_datarate, datarate);
	std::cout<<delay<<std::endl;
	std::cout<<datarate<<std::endl;
	
	if(datarate!="")
	{
		Config::Set("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/DataRate", StringValue(datarate));
		//Config::Set("/ChannelList/*/$ns3::CsmaChannel/DataRate",DataRateValue(datarate));
		//Config::Set("/ChannelList/*/Delay",StringValue(delay));
		ScheduleBw();
	}
	else
	{
		std::cout<<"end of file!"<<std::endl;
	}
}

void ScheduleBw(void)
{
	Simulator::Schedule(Seconds(3),BandwidthTrace);
}





int main(int argc, char** argv)
{

    Config::SetDefault("ns3::Rip::SplitHorizon",EnumValue(RipNg::POISON_REVERSE));
    GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::RealtimeSimulatorImpl"));
    GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true));

    
    bool nullmsg = false;


    CommandLine cmd;
    cmd.AddValue ("nullmsg", "Enable the use of null-message synchronization", nullmsg);

    cmd.Parse(argc, argv);

    // Sequential fallback values
    uint32_t systemId = 0;
    uint32_t systemCount = 1;

#ifdef NS3_MPI

    // Distributed simulation setup; by default use granted time window algorithm.
    if(nullmsg) 
    {
      GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::NullMessageSimulatorImpl"));
    } 
    else 
    {
      GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::DistributedSimulatorImpl"));
    }

    MpiInterface::Enable (&argc, &argv);

    systemId = MpiInterface::GetSystemId ();
    systemCount = MpiInterface::GetSize ();

    if (systemCount != 2)
    {
      std::cout << "This simulation requires 2 and only 2 logical processors." << std::endl;
      return 1;
    }

#endif // NS3_MPI


    // System id of Wifi side
    uint32_t systemLeft = 0;
  
    // System id of CSMA side
    uint32_t systemRight = systemCount - 1;



    Ptr<Node> serverNode = CreateObject<Node>(systemLeft);
    Ptr<Node> clientNode = CreateObject<Node>(systemRight);

    Ptr<Node> n1 =  CreateObject<Node>(systemLeft);
    Ptr<Node> n2 =  CreateObject<Node>(systemRight);
    NodeContainer net1(serverNode,n1);
    NodeContainer net2(n1,n2);
    NodeContainer net3(n2,clientNode);
    NodeContainer nodes(serverNode,clientNode);

    CsmaHelper csma;
    csma.SetChannelAttribute("DataRate",StringValue("0b/s"));
    csma.SetChannelAttribute("Delay",StringValue("0ms"));

    NetDeviceContainer ndc1 = csma.Install(net1);
    NetDeviceContainer ndc3 = csma.Install(net3);

    PointToPointHelper p2p;
    p2p.SetDeviceAttribute("DataRate",StringValue("0b/s"));
    p2p.SetChannelAttribute("Delay",StringValue("0ms"));

    NetDeviceContainer ndc2 = p2p.Install(net2);

    RipHelper ripRouting;
    ripRouting.ExcludeInterface(n1,1);
    ripRouting.ExcludeInterface(n2,2);    
 
    Ipv4ListRoutingHelper listRH;
    listRH.Add(ripRouting,0);

    InternetStackHelper internet;
    internet.SetIpv6StackInstall(false);
    internet.SetRoutingHelper(listRH);
    internet.Install(net2);


    InternetStackHelper internetNodes;
    internetNodes.SetIpv6StackInstall(false);
    internetNodes.Install(nodes);

    Ipv4AddressHelper ipv4;
    ipv4.SetBase(Ipv4Address("10.10.0.0"),Ipv4Mask("255.255.255.0"));
    Ipv4InterfaceContainer iic1 = ipv4.Assign(ndc1);
    ipv4.SetBase(Ipv4Address("10.10.1.0"),Ipv4Mask("255.255.255.0"));
    Ipv4InterfaceContainer iic2 = ipv4.Assign(ndc2);
    ipv4.SetBase(Ipv4Address("10.10.2.0"),Ipv4Mask("255.255.255.0"));
    Ipv4InterfaceContainer iic3 = ipv4.Assign(ndc3);
/*
    Ptr<Ipv4StaticRouting> staticRouting;
    staticRouting = Ipv4RoutingHelper::GetRouting<Ipv4StaticRouting>(serverNode->GetObject<Ipv4>()->GetRoutingProtocol());
    staticRouting->SetDefaultRoute("10.10.0.2",1);
    staticRouting = Ipv4RoutingHelper::GetRouting<Ipv4StaticRouting>(clientNode->GetObject<Ipv4>()->GetRoutingProtocol());
    staticRouting->SetDefaultRoute("10.10.2.1",1);
    */
    RipHelper routingHelper;
    Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(&std::cout);
 
    // 设置打印时间
    routingHelper.PrintRoutingTableAt(Seconds(1.0), n1, routingStream);
    routingHelper.PrintRoutingTableAt(Seconds(3.0), n1, routingStream);
    routingHelper.PrintRoutingTableAt(Seconds(5.0), n1, routingStream);
    routingHelper.PrintRoutingTableAt(Seconds(7.0), n1, routingStream);
    
    std::string mode = "UseBridge";
    std::string tapName = "tap-left";
    std::string tapName2 = "tap-right";
    if (systemId == systemLeft)
    {
      TapBridgeHelper tapBridge;
      tapBridge.SetAttribute ("Mode", StringValue (mode));
      tapBridge.SetAttribute ("DeviceName", StringValue (tapName));
      tapBridge.Install (net1.Get(0), ndc1.Get (0));
    }

  // If this rank is systemWifi
  // it should contain the client application, 
  // since it is on one of the wifi nodes
    if (systemId == systemRight)
    {
      TapBridgeHelper tapBridge;
      tapBridge.SetAttribute ("Mode", StringValue (mode));
      tapBridge.SetAttribute ("DeviceName", StringValue (tapName2));
      tapBridge.Install (net3.Get (1), ndc3.Get (1));
    }

    Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

    BandwidthTrace();
    Simulator::Stop(Seconds(60.));
    Simulator::Run();
    Simulator::Destroy();

#ifdef NS3_MPI
  // Exit the MPI execution environment
  MpiInterface::Disable ();
#endif
    return 0;

}
